using System;
using System.Collections.Generic;
using System.Text;

namespace Fuzzy
{
    public enum FuzzyOperationEnum { Complement, TNorm, SNorm };

    /// <summary>
    /// Union or intersection of 2 fuzzy sets, or complement of a fuzzy set.
    /// It computes its membership value from the membership values of its fuzzy sets
    /// and a fuzzy operation (T-norm, S-norm, complement) given in the constructor.
    /// </summary>
    public class ComplexFuzzySet:FuzzySet
    {
        private FuzzyOperationEnum operation;
        private FuzzySet[] operands;

        /// <summary>
        /// Creates a complex fuzzy set.
        /// </summary>
        /// <param name="fuzzyLinguisticVariable">The linguistic variable where the set belongs to.</param>
        /// <param name="name">The name of the fuzzy set.</param>
        /// <param name="operation">The operator which will be applied to the operands.</param>
        /// <param name="fuzzyOperands">The operands, the number of which must be 2 for union and intersection (T-norm, S-norm)
        /// and must 1 for complement.</param>
        public ComplexFuzzySet(FuzzyLinguisticVariable fuzzyLinguisticVariable, string name, FuzzyOperationEnum operation, params FuzzySet[] fuzzyOperands)
            : base(fuzzyLinguisticVariable, name)
        {
            if ((operation == FuzzyOperationEnum.Complement) && (fuzzyOperands.Length != 1))
                throw new FuzzyException("Complement can only be applied to exactly one fuzzy set.");
            if ((operation != FuzzyOperationEnum.Complement) && (fuzzyOperands.Length != 2))
                throw new FuzzyException("T-norm and S-norm can only be applied to exactly two fuzzy sets.");
            this.operation = operation;
            this.operands = fuzzyOperands;
        }

        /// <summary>
        /// Creates a complex fuzzy set.
        /// </summary>
        /// <param name="fuzzyLinguisticVariable">The linguistic variable where the set belongs to.</param>
        /// <param name="operation">The operator which will be applied to the operands.</param>
        /// <param name="fuzzyOperands">The operands, the number of which must be 2 for union and intersection (T-norm, S-norm)
        /// and must 1 for complement.</param>
        public ComplexFuzzySet(FuzzyLinguisticVariable fuzzyLinguisticVariable, FuzzyOperationEnum operation, params FuzzySet[] fuzzyOperands)
            :base(fuzzyLinguisticVariable)
        {
            if ((operation == FuzzyOperationEnum.Complement) && (fuzzyOperands.Length != 1))
                throw new FuzzyException("Complement can only be applied to exactly one fuzzy set.");
            if ((operation != FuzzyOperationEnum.Complement) && (fuzzyOperands.Length != 2))
                throw new FuzzyException("T-norm and S-norm can only be applied to exactly two fuzzy sets.");
            this.operation = operation;
            this.operands = fuzzyOperands;
        }

        /// <summary>
        /// The membership function of the fuzzy set.
        /// </summary>
        /// <param name="x">The number of which membership we are interested in.</param>
        /// <returns>The membership of x.</returns>

        public override double Membership(double x)
        {
            switch (operation)
            {
                case FuzzyOperationEnum.Complement:
                    return FuzzyUniverse.FuzzyOperation.Complement(operands[0].Membership(x));
                case FuzzyOperationEnum.TNorm:
                    return FuzzyUniverse.FuzzyOperation.TNorm(operands[0].Membership(x),operands[1].Membership(x));
                case FuzzyOperationEnum.SNorm:
                    return FuzzyUniverse.FuzzyOperation.SNorm(operands[0].Membership(x),operands[1].Membership(x));
            }
            return -1;
        }

        public override double[][] AlphaCut(double alpha)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public override double[][] StrictAlphaCut(double alpha)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        internal override FuzzySet Union(FuzzySet fuzzySet, bool mayRedirect)
        {
            if (fuzzySet.FuzzyLinguisticVariable != fuzzyLinguisticVariable)
                throw new FuzzyException("The 2 fuzzy sets belongs to different fuzzy linguistic variables.");

            if (mayRedirect)
                return fuzzySet.Union(this, false);

            return new ComplexFuzzySet(fuzzyLinguisticVariable, FuzzyOperationEnum.SNorm, this, fuzzySet);
        }

        internal override FuzzySet Intersection(FuzzySet fuzzySet, bool mayRedirect)
        {
            if (fuzzySet.FuzzyLinguisticVariable != fuzzyLinguisticVariable)
                throw new FuzzyException("The 2 fuzzy sets belongs to different fuzzy linguistic variables.");

            if (mayRedirect)
                return fuzzySet.Intersection(this, false);

            return new ComplexFuzzySet(fuzzyLinguisticVariable, FuzzyOperationEnum.TNorm, this, fuzzySet);
        }

        /// <summary>
        /// Computes the complement of the fuzzy set.
        /// </summary>
        /// <returns>The complement of the fuzzy set.</returns>
        public override FuzzySet Complement()
        {
            if (operation == FuzzyOperationEnum.Complement)
                return operands[0];
            return new ComplexFuzzySet(fuzzyLinguisticVariable, FuzzyOperationEnum.Complement, this);

        }

        public override double Sup()
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
